package dk.seqSite {
	import dk.dashboardFP.DashboardFP;
	import dk.dashboardFP.DashboardFPEvent;
	import dk.dashboardFP.dfpLogger.DFPLogger;
	import dk.dashboardFP.dfpLogger.DFPLoggerLevels;
	import dk.seqSite.contextMenu.ContextMenuNavigation;
	import dk.seqSite.page.PageControl;
	import dk.seqSite.page.PageData;
	import dk.seqSite.plugins.keyboardHandler.KeybardHandler;
	import dk.seqSite.plugins.keyboardHandler.KeyboardHandlerEvent;
	import dk.seqSite.tree.NodeLaunchOptions;
	import dk.seqSite.tree.TreeControl;
	import dk.seqSite.tree.TreeControlEvent;
	import dk.seqSite.tree.TreeNode;

	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.EventDispatcher;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;

	/**
	 * @author Josef Kj�rgaard
	 */
	public class SeqSiteBase extends EventDispatcher {

		private static var _oInstance : SeqSiteBase;
		private static var _bCreatingInstance : Boolean = false;
		public static const LOGGER_GROUP : int=985456;

		private var _xmlTree : XML;
		private var _oTreeControl : TreeControl;

		private var _cPartClassRefrence : Class;

		private var _cPageClassRefrence : Class;
		private var _oPageControl : PageControl;
		private var _bTreeControlStarted : Boolean = false;
		private var _xmlPageData : XML;
		
		//settings prop
		private var _bTracingEnabled : Boolean = false;
		private var _bRemberBlockedNodes : Boolean = true;
		private var _oDashboardFP : DashboardFP;
		private var _bStartTreeControlWhenReady : Boolean = true;
		private var _bAllowDynamicNodes : Boolean = true;
		private var _oContextMenuNavigation : ContextMenuNavigation;
		private var _oTemplateRegistry : TemplateRegistry;
		private var _sprPageContainer : Sprite;
		private var _oSeqSiteInitOptions : SeqSiteInitOptions;
		private var _oStage : Stage;
		private var _oKeyboardHandler : KeybardHandler;
		private var _bInitDone : Boolean=false;

		public function SeqSiteBase() {
			if( !_bCreatingInstance ) throw new Error("Singleton and can only be accessed through SeqSiteBase.get_oInstance()");
			setup();
		}
		
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// setup
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		private function setup() : void {
			setupTemplateRegistry();
			
			//:::::::
			// setup DFPLogger 
			DFPLogger.formatGroup(LOGGER_GROUP, "SeqSite", 0xD728C6);
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// inits
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		 
		 /**
		 * The main initiation method. Handles the node tree, TreeControl, PageControl..
		 * Pass a custom ElementFactory to sypply system with extended elements of TreeNode, APage and APart.
		 * 
		 * Note: SWFAddress will only work if the .js is supplied in the embedding code. 
		 * 
		 * @param sprPageContainer			The DisplayObjectContainer for SeqSiteBase to place APages and AParts in.
		 * @param xmlTree					The main site structure xml. The tree will be build from the content and structure of this xml.
		 * @param xmlPageData				Optional xml data. This is used when page data is loaded in at init. The TreeNodes will look in this object for refrenced data. 
		 * @param bUseSWFAddress			Wheter to use SWFAddress or not.
		 * @param oCustomElementFactory		Option to pass custom ElementFactory.
		 */
		public function initSystemByXML(oStage:Stage, sprPageContainer : Sprite, xmlTree:XML, oSeqSiteInitOptions : SeqSiteInitOptions) : void {

			_bInitDone=true;
			
			_oStage = oStage;
			
			setInitOptions(oSeqSiteInitOptions);
			
			setupContainers(sprPageContainer);
			
			_xmlTree = xmlTree;
			
			initTree();
		}
		

		public function initSystem(oStage:Stage, sprPageContainer : Sprite, oSeqSiteInitOptions:SeqSiteInitOptions) : void {
			
			_bInitDone=true;
			
			_oStage = oStage;
			
			setInitOptions(oSeqSiteInitOptions);
			
			setupContainers(sprPageContainer);

			initTree();
		}
		
		private function setupContainers(sprPageContainer : Sprite) : void {
			_sprPageContainer = sprPageContainer;
		}		

		private function setInitOptions(oSeqSiteInitOptions : SeqSiteInitOptions) : void {

			_oSeqSiteInitOptions = oSeqSiteInitOptions;

			if (_oSeqSiteInitOptions.bKeyboardShortcutsEnabled) {
				setupKeyboardHandler();
			}
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// keyboard shortcut handler
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		private function setupKeyboardHandler() : void {
			_oKeyboardHandler=new KeybardHandler(_oStage);
			_oKeyboardHandler.addEventListener(KeyboardHandlerEvent.BACK, onKeyBack, false, 0, true);
			_oKeyboardHandler.addEventListener(KeyboardHandlerEvent.FORWARD, onKeyForward, false, 0, true);
		}

		private function onKeyForward(event : KeyboardHandlerEvent) : void {
			_oTreeControl.stepForward();
		}

		private function onKeyBack(event : KeyboardHandlerEvent) : void {
			_oTreeControl.stepBack();
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// contextmenu handling
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		private function setupContextMenuNavigation() : void {
			if (_oSeqSiteInitOptions.bCreateContextMenuItems) {
				_oContextMenuNavigation = new ContextMenuNavigation(_oTreeControl, _oSeqSiteInitOptions.oContextLabelLanguage);
			}
		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// setup template registry
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		private function setupTemplateRegistry() : void {
			_oTemplateRegistry = TemplateRegistry.get_oInstance();
		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// tree setup
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function initTree() : void {
			
			_oPageControl = new PageControl();
			_oTreeControl = new TreeControl(_oPageControl, _oSeqSiteInitOptions);
			
			_oTreeControl.addEventListener(TreeControlEvent.etNEW_TREE_NODE_SELECTED, onTreeNodeSelected, false, 0, true);
			_oTreeControl.addEventListener(TreeControlEvent.FIRST_NODE_SELECTED, onFirstNodeSelected, false, 0, true);
			
			_oTemplateRegistry.oTemplateFactory = _oSeqSiteInitOptions.oTemplateFactory;
			
			// register node factory
			_oTreeControl.registerTreeNodeFactory(_oSeqSiteInitOptions.oTreeNodeFactory);
			
			if (_xmlTree) {
				_oTreeControl.addEventListener(TreeControlEvent.SITETREE_PARSED, onSitetreeParsed);
				_oTreeControl.initTreeByXML(_xmlTree, _oSeqSiteInitOptions.bUseSWFAddress);
			} else {
				_oTreeControl.initByCreatingRoot(_oSeqSiteInitOptions.bUseSWFAddress);
			}
			
			//setReady();
		}

		private function onSitetreeParsed(event : TreeControlEvent) : void {
			setReady();
		}

		private function onFirstNodeSelected(event : TreeControlEvent) : void {
			dispatchEvent(new SeqSiteBaseEvent(SeqSiteBaseEvent.FIRST_NODE_SELECTED, _oTreeControl.oTNCurrent));
		}

		private function onTreeNodeSelected(event : TreeControlEvent) : void {
			dispatchEvent(new SeqSiteBaseEvent(SeqSiteBaseEvent.NEW_TREE_NODE_SELCTED, _oTreeControl.oTNCurrent));
		}

		private function setReady() : void {
			dispatchEvent(new SeqSiteBaseEvent(SeqSiteBaseEvent.READY));
			if (_bStartTreeControlWhenReady) {
				startTreeControl();
			}
		}		
		
		/**
		 * Starts the tree control.
		 * 
		 * @see TreeControl
		 */
		public function startTreeControl(oTNDefault : TreeNode = null) : void {
			
			_bTreeControlStarted = true;
			_oTreeControl.startTreeControl(oTNDefault);
			
			setupContextMenuNavigation();
			
			dispatchEvent(new SeqSiteBaseEvent(SeqSiteBaseEvent.TREE_CONTROL_STARTED));
		}
		
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// register template classes
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		/**
		 * Register APart types. This is where you register the APart subclasses that needs to be avalible for
		 * autogeneration by APages.
		 * 
		 * @param args			AbstractPart			Takes a collection of APart subclasses.
		 */
		public function register_cPartTypes(... args) : void {
			for each (var cType : Class in args) {
				_oTemplateRegistry.addPartTemplate(cType);
			}
		}
		
		/**
		 * Register APage types. This is where you register the APage subclasses that needs to be avalible for
		 * autogeneration by PageControl.
		 * 
		 * @param Takes a collection of AbstractPage subclasses.
		 */
		
		public function  register_cPageTypes(... args) : void {
			for each (var cType : Class in args) {
				_oTemplateRegistry.addPageTemplate(cType);
			}
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// singleton stuff
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function get_oInstance() : SeqSiteBase {
			if (!_oInstance) {
				_bCreatingInstance = true;
				_oInstance = new SeqSiteBase();
				_bCreatingInstance = false;
			}
			return _oInstance;
		}
		
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// request node launch from anchor path
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		public function handleLink(sURL : String, sTarget : String="_self", oNodeParms:Object=null) : void {
			
			if (!sURL || sURL=="") {
				DFPLogger.log("No url supplied for 'handleLink' method. Aborting.", null, LOGGER_GROUP, DFPLoggerLevels.ERROR);
				return;
			}
			
			if (sURL.substr(0, 4)=="http" || sURL.substr(0, 3)=="www") {
				navigateToURL(new URLRequest(sURL), sTarget);
			} else {
				var oNLO:NodeLaunchOptions;
				
				if (sTarget.toLowerCase()=="_blank") {
					oNLO=new NodeLaunchOptions(true, true, oNodeParms);
				} else {
					oNLO=new NodeLaunchOptions(false, true, oNodeParms);
				}
				requestNodeLaunchFromAnchorPath(sURL, oNLO);
			}
		}
		
		public function requestNodeLaunchFromAnchorPath(sAnchorPath : String, oNLO : NodeLaunchOptions=null) : void {
			
			if (!sAnchorPath || sAnchorPath == "") {
				DFPLogger.log("No anchor-path supplied for 'handleLink' method. Aborting.", null, LOGGER_GROUP, DFPLoggerLevels.ERROR);
				return;
			}
			
			var sCleanAnchorPath : String=get_sCleanAnchorPath(sAnchorPath);
			var oParms : Object=get_oParmsObjectFromString(sAnchorPath);
			
			if (!oNLO) {
				oNLO=new NodeLaunchOptions();
			}
			
			for (var sKey : String in oParms) {
				oNLO.oParms[sKey] = oParms[sKey];
			}
			
			_oTreeControl.requestNodeLaunchFromAnchorPath(sCleanAnchorPath, oNLO);
		}
		
		private function get_oParmsObjectFromString(sAnchorPath : String) : Object {
			
			var oParms : Object = new Object();
			if (sAnchorPath.split("?").length==1) {
				return oParms;
			} else {
				var aParmPairs : Array=sAnchorPath.split("?")[1].split("&");
				for each (var sParPair : String in aParmPairs) {
					oParms[sParPair.split("=")[0]]=sParPair.split("=")[1];
				}
			}
			
			return oParms; 
		}
		
		private function get_sCleanAnchorPath(sAnchorString:String) : String {
			
			if (!sAnchorString) {
				return "/";
			}
			
			return sAnchorString.split("?")[0];
		}


		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// getter setters
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		public function get_oPageDataByRefrenceId(sRefId : String) : PageData {
			
			// no data xml set
			if (_xmlPageData==null) {
				DFPLogger.log("No page xmlPageData set. Request for data refrence : " + sRefId, null, LOGGER_GROUP, DFPLoggerLevels.ERROR);
				return null;
			}
			
			// find data by refId
			for each (var xmlPage : XML in _xmlPageData.page) {
				var sRefIDXML : String = xmlPage.@[EMenuXMLKeys.REF_ID];
				if (sRefId == sRefIDXML) {
					return new PageData(xmlPage);
				}
			}
			return null;
		}

		public function get bTracingEnabled() : Boolean {
			return _bTracingEnabled;		}
		public function set bTracingEnabled(bTracingEnabled : Boolean) : void {
			_bTracingEnabled = bTracingEnabled;		}

		public function get sprPageContainer() : DisplayObjectContainer {
			return _sprPageContainer;
		}

		public function get bTreeControlStarted() : Boolean {
			return _bTreeControlStarted;
		}

		public function get oRootNode() : TreeNode { 
			return _oTreeControl.oRootNode; 
		}
		
		public function get bRemberBlockedNodes() : Boolean { 
			return _bRemberBlockedNodes; 
		}
		
		public function set bRemberBlockedNodes(oNewValue:Boolean) : void { 
			_bRemberBlockedNodes=oNewValue;
		}
		
		public function get bAllowDynamicNodes() : Boolean { 
			return _bAllowDynamicNodes; 
		}
		
		public function set bAllowDynamicNodes(oNewValue:Boolean) : void { 
			_bAllowDynamicNodes=oNewValue;
		}
		
		public function stepBack() : void {
			_oTreeControl.stepBack();
		}
		
		public function stepForward() : void {
			_oTreeControl.stepForward();
		}
		public function get_oTNFromUrl(sURL : String, bReturnClosestMatch:Boolean=true) : TreeNode {
			return _oTreeControl.get_oTNFromUrl(sURL, bReturnClosestMatch);
		}
		
		public function get bStartTreeControlWhenReady() : Boolean {
			return _bStartTreeControlWhenReady;
		}
		
		public function set bStartTreeControlWhenReady(bStartTreeControlWhenReady : Boolean) : void {
			_bStartTreeControlWhenReady = bStartTreeControlWhenReady;
		}
		
		public function get xmlPageData() : XML {
			return _xmlPageData;
		}
		
		public function set xmlPageData(xmlPageData : XML) : void {
			_xmlPageData = xmlPageData;
		}
		
		public function get oTNCurrent() : TreeNode { 
			return _oTreeControl.oTNCurrent; 
		}
		
		public function get_a_oTNsByNodeGroup(sNodeGroup : String) : Array {
			return _oTreeControl.get_a_oTNsByNodeGroup(sNodeGroup);
		}
		
		public function get oTNPreviousNode() : TreeNode { 
			return _oTreeControl.oTNLast; 
		}
		
		public function get bInitDone() : Boolean {
			return _bInitDone;
		}
	}
}
